package xmpp.watchers;

import syslog.ILog;
import activity.async.AsyncWorker;

/**
 * Base class for all activity watchers ( xmpp connections, chat rooms and so
 * on). Activity watcher is active object. On each iteration it polls some
 * underlying activity using {@link #checkActivityAlive()} method which should
 * be <b> overridden </b> in subclasses. If activity is alive watcher sends
 * appropriative message to log using {@link #logActivityAlive()} method. If
 * activity isn't alive (e.g. down) watcher sends message to log using
 * {@link #logActivityDown()} and calls {@link #startActivity()}.
 * <p>
 * If any exception is generated during activity start up or activity lifetime
 * {@link #logActivityException(Exception)} is called
 * 
 * <p>
 * Abstract methods which must be be overridden in subclasses:
 * <ul>
 * <li><code>checkActivityAlive()</code>
 * <li><code>startActivity()</code>
 * <li><code>logActivityAlive()</code>
 * <li><code>logActivityDown()</code>
 * <li><code>logActivityException()</code>
 * </ul>
 * 
 * @author tilllias
 * 
 */
public abstract class AbstractActivityWatcher extends AsyncWorker {
    /**
     * Default connection poll timeout in milliseconds
     */
    public static final int DEFAULT_POLL_TIMEOUT = 30000;

    /**
     * Constructor for invocation by subclasses. Creates new activity watcher
     * using given {@link ILog} implementation and sets poll timeout to
     * {@link #DEFAULT_POLL_TIMEOUT}
     * 
     * @param log
     *            {@link ILog} implementation which will be used to log all
     *            events generated by this watcher
     * @throws NullPointerException
     *             Thrown if log argument passed to constructor is null
     */
    public AbstractActivityWatcher(ILog log) throws NullPointerException {
	this(log, DEFAULT_POLL_TIMEOUT);
    }

    /**
     * Constructor for invocation by subclasses. Creates new activity watcher
     * using given {@link ILog} implementation and sets poll timeout to given
     * value
     * 
     * @param log
     *            {@link ILog} implementation which will be used to log all
     *            events generated by this watcher
     * @param pollTimeout
     *            Poll timeout for this watcher
     * @throws NullPointerException
     *             Thrown if log argument passed to constructor is null
     * @throws IllegalArgumentException
     *             Thrown if pollTimeout argument is negative or zero
     */
    public AbstractActivityWatcher(ILog log, int pollTimeout)
	    throws NullPointerException, IllegalArgumentException {

	super(pollTimeout);

	if (log == null)
	    throw new NullPointerException("Log argument can't be null");
	if (pollTimeout <= 0)
	    throw new IllegalArgumentException("pollTimeout must be positive");

	this.log = log;

	logSenderName = getClass().getSimpleName();
	pollCounter = 0;
	lastPollInactive = true;
    }

    /**
     * Gets value indicating that activity under control is alive
     * 
     * @return True if activity is alive, false otherwise
     */
    public abstract boolean checkActivityAlive();

    /**
     * Starts activity
     */
    public abstract void startActivity();

    /**
     * Method is called by watcher if activity is alive on next poll
     */
    public abstract void logActivityAlive();

    /**
     * Method is called by watcher if activity is down on next poll
     */
    public abstract void logActivityDown();

    /**
     * Method is called by watcher if activity has thrown any exception during
     * start up or it's run
     * 
     * @param e
     *            Exception thrown by activity
     */
    public abstract void logActivityException(Exception e);

    @Override
    public void performAction() {
	try {
	    // do work
	    boolean isAlive = checkActivityAlive();

	    if (!isAlive) {
		restartActivity();
	    } else {

		logActivityAlive();

		setLastPollInactive(false);
	    }

	    ++pollCounter;

	} catch (Exception e) {
	    logActivityException(e);

	}
    }

    /**
     * Gets total number of milliseconds that watcher waits between polls
     * 
     * @return Total number of milliseconds
     */
    public int getPollTimeout() {
	return getTimeout();
    }

    /**
     * Gets total number of polls since watcher has been started. Counter is
     * cumulative e.g. it stores its value between restarts
     * 
     * @return Total number of polls since watcher has been started
     */
    public int getPollCount() {
	return pollCounter;
    }

    protected ILog getLog() {
	return log;
    }

    /**
     * Gets value indicating that on last poll activity under control by this
     * watcher was inactive
     * 
     * @return True if on last poll activity was inactive, false otherwise
     */
    protected boolean getLastPollInactive() {
	return lastPollInactive;
    }

    /**
     * Sets value indicating that on last poll activity under control by this
     * watcher was inactive
     * 
     * @param value
     *            New value
     */
    protected void setLastPollInactive(boolean value) {
	lastPollInactive = value;
    }

    protected String getLogSenderName() {
	return logSenderName;
    }

    private void restartActivity() {
	logActivityDown();

	if (!getLastPollInactive()) {
	    setLastPollInactive(true);
	}

	startActivity();
    }

    ILog log;

    int pollCounter;
    boolean lastPollInactive;

    String logSenderName;
}
